/**
 * This file is part of Opintopaivakirja.
 *
 * Opintopaivakirja is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Opintopaivakirja is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Opintopaivakirja.  If not, see <http://www.gnu.org/licenses/>.
 */
package opintopaivakirja;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.regex.Pattern;

/**
 * Luokka toteuttaa järjestelmän ominaisuudet, jotka liittyvät
 * järjestelmän käyttäjien hallintaan.
 *
 * @author Teemu Jonkkari
 * @version 1.0
 */
public class Kayttaja {

    private int id;
    private int ppoisto;
    private String etunimi;
    private String sukunimi;
    private String sahkoposti;
    private String otunnus;
    private String rtunnus;
    private String kayttajatunnus;
    private String salasana;
    private ArrayList<Projekti> projektit;

    /**
     * Oletusmuodostin
     */
    public Kayttaja() {
        this.projektit = new ArrayList<Projekti>();
    }

    /**
     * Muodostin, jolla voidaan asettaa suoraan käyttäjän tiedot.
     *
     * @param etunimi Käyttäjän etunimi
     * @param sukunimi Käyttäjän sukunimi
     * @param sahkoposti Käyttäjän sähköpostiosoite
     * @param otunnus Käyttäjän opiskelijanumero
     * @param rtunnus Käyttäjän ryhmätunnus
     * @param kayttajatunnus Käyttäjän käyttäjätunnus
     * @param salasana Käyttäjän salasana
     */
    public Kayttaja(String etunimi, String sukunimi,
            String sahkoposti, String otunnus, String rtunnus,
            String kayttajatunnus, String salasana) {
        this.etunimi = etunimi;
        this.sukunimi = sukunimi;
        this.sahkoposti = sahkoposti;
        this.otunnus = otunnus;
        this.rtunnus = rtunnus;
        this.kayttajatunnus = kayttajatunnus;
        this.salasana = salasana;

        this.projektit = new ArrayList<Projekti>();
    }

    /**
     * Muodostin, jolla voidaan asettaa suoraan käyttäjän kaikki tiedot.
     *<p>
     * Tarkoitettu tietokantakyselyn käytettäväksi, sillä tällöin
     * tiedetään myös id ja voidaan asettaa se.
     * 
     * @param id Käyttäjän yksilöivä id-tunniste
     * @param ppoisto Käyttäjän asetus projektin poistamiseksi, 1 = poistetaan kokonaan
     * @param etunimi Käyttäjän etunimi
     * @param sukunimi Käyttäjän sukunimi
     * @param sahkoposti Käyttäjän sähköpostiosoite
     * @param otunnus Käyttäjän opiskelijanumero
     * @param rtunnus Käyttäjän ryhmätunnus
     * @param kayttajatunnus Käyttäjän käyttäjätunnus
     * @param salasana Käyttäjän salasana
     */
    private Kayttaja(int id, int ppoisto, String etunimi, String sukunimi,
            String sahkoposti, String otunnus, String rtunnus,
            String kayttajatunnus, String salasana) {
        this.id = id;
        this.ppoisto = ppoisto;
        this.etunimi = etunimi;
        this.sukunimi = sukunimi;
        this.sahkoposti = sahkoposti;
        this.otunnus = otunnus;
        this.rtunnus = rtunnus;
        this.kayttajatunnus = kayttajatunnus;
        this.salasana = salasana;

        this.projektit = new ArrayList<Projekti>();
    }

    /**
     * Metodilla voidaan noutaa käyttäjän tiedot tietokannasta.
     * 
     * @param kayttajatunnus merkkijono, joka on käyttäjän käyttäjätunnus.
     * @param salasana merkkijono, joka on käyttäjän salasana järjestelmään.
     * @return Kayttaja-olio, jossa kaikki kannassa olleet käyttäjän tiedot.
     */
    private Kayttaja selectKayttaja(String kayttajatunnus, String salasana) {
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement ps = null;

        Kayttaja kayttaja = new Kayttaja();

        try {
            conn = new Tietokanta().getYhteys();

            ps = conn.prepareStatement("" +
                    "SELECT ID, ETUNIMI, SUKUNIMI, SAHKOPOSTI, OTUNNUS, " +
                    "RTUNNUS, KAYTTAJATUNNUS, SALASANA, PPOISTO " +
                    "FROM OPINTOPAIVAKIRJA.KAYTTAJA " +
                    "WHERE KAYTTAJATUNNUS = ? AND SALASANA = ? " +
                    "ORDER BY KAYTTAJATUNNUS ASC");
            ps.setString(1, kayttajatunnus);
            ps.setString(2, salasana);

            rs = ps.executeQuery();

            while (rs.next()) {
                kayttaja.id = rs.getInt("ID");
                kayttaja.etunimi = rs.getString("ETUNIMI");
                kayttaja.sukunimi = rs.getString("SUKUNIMI");
                kayttaja.sahkoposti = rs.getString("SAHKOPOSTI");
                kayttaja.kayttajatunnus = rs.getString("KAYTTAJATUNNUS");
                kayttaja.salasana = rs.getString("SALASANA");
                kayttaja.otunnus = rs.getString("OTUNNUS");
                kayttaja.rtunnus = rs.getString("RTUNNUS");
                kayttaja.ppoisto = rs.getInt("PPOISTO");
            }

        } catch (SQLException ex) {
            Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
        } finally {
            if (ps != null) {
                try {
                    ps.close();
                    ps = null;
                } catch (SQLException ex) {
                    Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                }
            }

            if (rs != null) {
                try {
                    rs.close();
                    rs = null;
                } catch (SQLException ex) {
                    Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                }
            }
            return kayttaja;
        }
    }

    /**
     * Metodilla voidaan lisätä tietokantaan käyttäjä.
     * 
     * @return totuusarvo, siitä onnistuiko käyttäjän lisääminen tietokantaan.
     */
    private boolean insertKayttaja() {
        Connection conn = null;
        PreparedStatement ps = null;

        this.kayttajatunnus = this.kayttajatunnus.trim().toLowerCase();

        // pakolliset kentät: etunimi, sukunimi ja kayttajatunnus
        if (tarkistaKayttajatunnus(this.kayttajatunnus) && this.etunimi.length() > 1 && this.sukunimi.length() > 1) {

            boolean vastaus = true;

            try {
                conn = new Tietokanta().getYhteys();

                ps = conn.prepareStatement("" +
                        "INSERT INTO OPINTOPAIVAKIRJA.KAYTTAJA " +
                        "(ETUNIMI, SUKUNIMI, SAHKOPOSTI, OTUNNUS, RTUNNUS, " +
                        "KAYTTAJATUNNUS, SALASANA, PPOISTO) " +
                        "VALUES (?, ?, ?, ?, ?, ?, ?, 0)");
                ps.setString(1, this.etunimi.trim());
                ps.setString(2, this.sukunimi.trim());
                ps.setString(3, this.sahkoposti.trim());
                ps.setString(4, this.otunnus.trim());
                ps.setString(5, this.rtunnus.trim());
                ps.setString(6, this.kayttajatunnus);
                ps.setString(7, this.salasana);

                ps.executeUpdate();

            } catch (SQLException ex) {
                Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                vastaus = false;
            } finally {
                try {
                    if (ps != null) {
                        ps.close();
                        ps = null;
                    }
                } catch (SQLException ex) {
                    Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                }
                return vastaus;
            }
        }
        return false;
    }

    /**
     * Metodilla voidaan päivittää aktiivisen käyttäjän tiedot tietokantaan.
     * 
     * @return totuusarvo siitä onnistuiko käyttäjän päivittäminen.
     */
    private boolean updateKayttaja() {
        Connection conn = null;
        PreparedStatement ps = null;

        this.kayttajatunnus = this.kayttajatunnus.trim().toLowerCase();

        if (tarkistaKayttajatunnus(this.kayttajatunnus)) {

            boolean vastaus = true;

            try {
                conn = new Tietokanta().getYhteys();

                ps = conn.prepareStatement("" +
                        "UPDATE OPINTOPAIVAKIRJA.KAYTTAJA " +
                        "SET ETUNIMI = ?, SUKUNIMI = ?, SAHKOPOSTI = ?, " +
                        "OTUNNUS = ?, RTUNNUS = ?, KAYTTAJATUNNUS = ?, " +
                        "SALASANA = ?, PPOISTO = ? WHERE ID = ?");
                ps.setString(1, getEtunimi().trim());
                ps.setString(2, getSukunimi().trim());
                ps.setString(3, getSahkoposti().trim());
                ps.setString(4, getOtunnus().trim());
                ps.setString(5, getRtunnus().trim());
                ps.setString(6, getKayttajatunnus());
                ps.setString(7, getSalasana().trim());
                ps.setInt(8, getPpoisto());
                ps.setInt(9, getId());

                ps.executeUpdate();

            } catch (SQLException ex) {
                Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                vastaus = false;
            } finally {
                try {
                    if (ps != null) {
                        ps.close();
                        ps = null;
                    }
                } catch (SQLException ex) {
                    Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                }

                return vastaus;
            }
        }
        return false;
    }

    /**
     * Metodilla voidaan poistaa aktiivinen käyttäjä tietokannasta.
     * 
     * @return totuusarvo siitä, onnistuiko käyttäjän poistaminen.
     */
    private boolean deleteKayttaja() {
        Connection conn = null;
        PreparedStatement ps = null;

        boolean vastaus = true;

        for (Projekti projekti : projektit) {
            projekti.deleteProjekti();
        }

        try {
            conn = new Tietokanta().getYhteys();

            ps = conn.prepareStatement("" +
                    "DELETE FROM OPINTOPAIVAKIRJA.KAYTTAJA WHERE ID = ?");
            ps.setInt(1, getId());

            ps.executeUpdate();

        } catch (SQLException ex) {
            Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
            vastaus = false;
        } finally {
            try {
                if (ps != null) {
                    ps.close();
                    ps = null;
                }
            } catch (SQLException ex) {
                Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
            }
            return vastaus;
        }
    }

    /**
     * Metodilla tarkistetaan täyttääkö kayttajatunnus vaatimukset.
     * 
     * @param kayttajatunnus merkkijono, joka tarkitetaan.
     * @return totuusarvo siitä täyttikö merkkijono käyttäjätunnukselle asetetut vaatimukset.
     */
    private boolean tarkistaKayttajatunnus(String kayttajatunnus) {
        // käyttäjätunnuksen pitää olla väh. 5 merkkiä.
        // sallittuja merkkejä a-zA-Z_0-9

        if (kayttajatunnus.length() < 5) {
            return false;
        }

        if (!Pattern.matches("[\\w]*", kayttajatunnus)) {
            return false;
        }
        return true;
    }

    /**
     * Metodilla käyttäjä voi kirjautua järjestelmään.
     * Käyttäjätili pitää olla olemassa kirjautumisvaiheessa.
     *
     * @param kayttajatunnus Käyttäjän käyttäjätunnus
     * @param salasana Käyttäjän salasana
     *
     * @return boolean siitä, onnistuiko käyttäjän kirjautuminen.
     */
    public Kayttaja kirjauduSisaan(String kayttajatunnus, String salasana) {
        return selectKayttaja(kayttajatunnus.trim().toLowerCase(), salasana);
    }

    /**
     * Metodilla voidaan lisätä käyttäjätili ohjelmaan.
     *
     * @return totuusarvo siitä, onnistuiko käyttäjän lisääminen.
     */
    public boolean lisaaKayttaja() {
        return this.insertKayttaja();
    }

    /**
     * Metodilla voidaan poistaa käyttäjän tili ohjelmasta.
     * Metodi ooistaa samalla kaikki käyttäjäkohtaiset tiedot tietokannasta.
     *
     * @return boolean siitä, onnistuiko käyttäjän poistaminen.
     */
    public boolean poistaKayttaja() {
        return this.deleteKayttaja();
    }

    /**
     * Metodilla voidaan tallentaa käyttäjätilin muuttuneet tiedot tietokantaan.
     *
     * @return boolean siitä, onnistuiko käyttäjätilin tietojen päivittäminen.
     */
    public boolean tallennaKayttaja() {
        return this.updateKayttaja();
    }

    /**
     * Metodilla voidaan hakea käyttäjän projektit tietokannasta.
     *
     * @return boolean siitä, onnistuiko käyttäjän projektien hakeminen.
     */
    public boolean haeProjektit() {
        Projekti p = new Projekti();
        this.projektit = p.selectProjektit(this.id);

        if (this.projektit.isEmpty()) {
            return false;
        }
        return true;
    }

    /**
     * Metodilla voidaan tallentaa projekti tietokantaan.
     *
     * @param projekti Projekti, joka tallennetaan tietokantaan.
     * @return boolean siitä, onnistuiko projektin tallentaminen.
     */
    public boolean tallennaProjekti(Projekti projekti) {
        if (projekti.getTila() == 0) {
            if (!projekti.insertProjekti(this.id)) {
                return false;
            }
        } else if (projekti.getTila() == 1) {
            if (!projekti.updateProjekti(this.id)) {
                return false;
            }
        } else if (projekti.getTila() > 1) {
            if (!projekti.deleteProjekti()) {
                return false;
            }
        }
        return true;
    }

    /**
     * Metodilla voidaan poistaa käyttäjän projekti.
     * <p>
     * Jos käyttäjällä on asetettuna pysyvä poistaminen, 
     * poistetaan projekti tietokannasta kokonaan. Jos
     * pysyvää poistamista ei ole erikseen asetettu, muutetaan
     * projektin tila poistetuksi (9) ja lisätään projektin
     * nimeen P-kirjain.
     *
     * @param projekti Projekti, joka halutaan poistaa.
     * @return boolean siitä, onnistuiko projektin poistaminen.
     */
    public boolean poistaProjekti(Projekti projekti) {
        // jos pysyväpoisto ei ole voimassa
        if (this.ppoisto == 0) {
            projekti.setAsema(2); //0 = tavallinen, 1 = valmis, 2 = poistettu

            // nimetään projekti vielä uudelleen
            String uusinimi = "(Poistettu) " + projekti.getNimi();
            projekti.setNimi(uusinimi);

            if (!tallennaProjekti(projekti)) {
                return false;
            }
            return true;
        } else { // pysyvä poisto
            projekti.deleteProjekti();
        }
        return true;
    }

    /**
     * Metodilla voidaan testata tietokantayhteyden saatavuutta.
     * <p>
     * Jos testissä todetaan ettei tietokantaa ole olemassa 
     * niin se tehdään samassa yhteydessä.
     *
     * @return totuusarvo siitä, saadaanko yhteys tietokantaan.
     */
    public static boolean testaaTietokantayhteys() {
        Connection conn = new Tietokanta().getYhteys();

        try {
            PreparedStatement ps = conn.prepareStatement("" +
                    "SELECT COUNT(*) FROM OPINTOPAIVAKIRJA.KAYTTAJA");
            ps.executeQuery();
            ps = null;
        } catch (SQLException ex) {
            // jos tulee error 30000, luodaan kanta, koska sitä ei ole.
            if (ex.getErrorCode() == 30000) {
                new Tietokanta().luoTietokanta();
                new Tietokanta().sammutaTietokanta();
            } else {
                Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), ex);
                return false;
            }
        } catch (NullPointerException e) {
            Virheenkasittely.getInstance().setVirhe(Tietokanta.class.getName(), e);
            return false;
        }
        return true;
    }

    /**
     * Metodilla voidaan palauttaa käyttäjän 5 viimeisintä
     * projektia. Viimeisin ensimmäisenä.
     *
     * @return Taulukko, jossa käyttäjän 5 viimeisintä projektia. Pelkät projektitiedot.
     */
    public ArrayList<Projekti> haeTop5Projektit() {
        Projekti proj = new Projekti();

        return proj.selectTop5Projektit(this.id);
    }

    /**
     * Metodilla haetaan käyttäjän projekteista hakusanoilla ja
     * tägeillä projekteja, joihin sisältyy kyseiset hakuparametrit.
     *
     * @param hakulause Käyttäjän syöttämä merkkijono, joka pilkotaan välimerkkien kohdilta.
     * @param tagilause Käyttäjän valitsemat tägit merkkijonona, joka pilkotaan samoin.
     * @param valmiit Parametri haetaanko myös valmiiksi merkityistä projekteista.
     * @param poistetut Parametri haetaanko myös poistetuista projekteista.
     * @return ArrayList, jossa projektit, joihin hakuparametrit sopivat.
     */
    public ArrayList<Projekti> haeTiedot(String hakulause,
            String tagilause, boolean valmiit, boolean poistetut) {
        Haku haku = new Haku();
        String[] hakusanat;

        // Haku sallii vain yhden "-merkeillä rajatun tarkan haun.
        if (hakulause.contains("\"")) {
            int p1 = hakulause.indexOf("\"");
            System.out.println(p1);
            if (hakulause.substring(++p1).contains("\"")) {
                int p2 = hakulause.indexOf("\"", p1);
                String lause = hakulause.substring(p1, p2);
                hakusanat =
                        new String[1];
                hakusanat[0] = lause;
            } else {
                // poistetaan ensimmäinenkin viesti
                hakulause = hakulause.replace("\"", "");

                // pilkotaan merkkijono välilyöntien mukaan
                hakusanat =
                        hakulause.split(" ");
            }

        } else {
            // pilkotaan merkkijono välilyöntien mukaan
            hakusanat = hakulause.split(" ");
        }

        // Tagit
        String[] tagit = tagilause.split(" ");

        return haku.selectHaku(hakusanat, tagit, valmiit, poistetut);
    }

    /**
     * Metodilla voidaan lisätä valmis projektitemplate tietokantaan.
     * <p>
     * Valmiit projektitemplatet:
     * <ol>
     * <li>Yleistemplate, joka sisältää Yleismuistiinpanot ja Termipankki projektit.</li>
     * <li>DI-tutkinnon template.</li>
     * <li>MBA-tutkinnon template.</li>
     * <li>AMK-tutkinnon template.</li>
     * <li>2. asteen opintojen template.</li>
     * </ol>
     *
     * @param template kokonaisluku, joka kertoo luotavan templaten.
     */
    public void lisaaTemplate(int template) {
        new Projekti().insertTemplate(this.id, template);
    }

    /**
     * @return the etunimi
     */
    public String getEtunimi() {
        return etunimi;
    }

    /**
     * @param etunimi the etunimi to set
     */
    public void setEtunimi(String etunimi) {
        this.etunimi = etunimi;
    }

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the kayttajatunnus
     */
    public String getKayttajatunnus() {
        return kayttajatunnus;
    }

    /**
     * @param kayttajatunnus the kayttajatunnus to set
     */
    public void setKayttajatunnus(String kayttajatunnus) {
        this.kayttajatunnus = kayttajatunnus;
    }

    /**
     * @return the otunnus
     */
    public String getOtunnus() {
        return otunnus;
    }

    /**
     * @param otunnus the otunnus to set
     */
    public void setOtunnus(String otunnus) {
        this.otunnus = otunnus;
    }

    /**
     * @return the projektit
     */
    public ArrayList<Projekti> getProjektit() {
        return projektit;
    }

    /**
     *
     * @param projektit the projektit to set
     */
    public void setProjektit(ArrayList<Projekti> projektit) {
        this.projektit = projektit;
    }

    /**
     * @return the rtunnus
     */
    public String getRtunnus() {
        return rtunnus;
    }

    /**
     * @param rtunnus the rtunnus to set
     */
    public void setRtunnus(String rtunnus) {
        this.rtunnus = rtunnus;
    }

    /**
     * @return the sahkoposti
     */
    public String getSahkoposti() {
        return sahkoposti;
    }

    /**
     * @param sahkoposti the sahkoposti to set
     */
    public void setSahkoposti(String sahkoposti) {
        this.sahkoposti = sahkoposti;
    }

    /**
     * @return the salasana
     */
    public String getSalasana() {
        return salasana;
    }

    /**
     * @param salasana the salasana to set
     */
    public void setSalasana(String salasana) {
        this.salasana = salasana;
    }

    /**
     * @return the sukunimi
     */
    public String getSukunimi() {
        return sukunimi;
    }

    /**
     * @param sukunimi the sukunimi to set
     */
    public void setSukunimi(String sukunimi) {
        this.sukunimi = sukunimi;
    }

    /**
     * @return the ppoisto
     */
    public int getPpoisto() {
        return ppoisto;
    }

    /**
     * @param ppoisto the ppoisto to set
     */
    public void setPpoisto(int ppoisto) {
        this.ppoisto = ppoisto;
    }

    /**
     * @param projekti the projekti to set
     */
    public void setProjekti(Projekti projekti) {
        this.projektit.add(projekti);
    }
}
